﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMeterParamFrame
{
    /// <summary>
    /// 645-07协议框架
    /// </summary>
    internal class DLT6452007
    {
        /// <summary>
        /// 固定发送FE个数
        /// </summary>
        private int FECount = 3;
        /// <summary>
        /// 表通信地址
        /// </summary>
        private string meterAddr = "".PadLeft(12, 'A');
        /// <summary>
        /// 操作者代码
        /// </summary>
        private string userCode = "12345678";

        /// <summary>
        /// 密码等级
        /// </summary>
        private string writeLevel = "02";

        /// <summary>
        /// 密码
        /// </summary>
        private string writePwd = "000000";

        /// <summary>
        /// 构造函数，无参数
        /// </summary>
        public DLT6452007() { }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="MeterAddr">表通信地址</param>
        public DLT6452007(string MeterAddr)
        {
            meterAddr = MeterAddr;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="MeterAddr">表地址</param>
        /// <param name="UserCode">操作者代码</param>
        /// <param name="WriteLevel">鞋标等级</param>
        /// <param name="WritePwd">写表密码</param>
        public DLT6452007(string MeterAddr, string UserCode, string WriteLevel, string WritePwd)
        {
            meterAddr = FormatData(MeterAddr, 12);

            userCode = FormatData(UserCode, 8);

            writeLevel = FormatData(WriteLevel, 2);

            writePwd = FormatData(WritePwd, 6);
        }

        /// <summary>
        /// 组包
        /// </summary>
        /// <param name="meterAddr">表通信地址</param>
        /// <param name="CmdCode">命令码</param>
        /// <param name="BsCode">标识符</param>
        /// <param name="DataDomain">数据域</param>
        /// <param name="HavePwd">是否是带密码</param>
        /// <param name="HaveUserCode">是否是带操作者代码</param>
        /// <param name="BlnBlock">是否是多块数据(是多块的话，数据DataDomain将使用|分割)</param>
        /// <param name="BwCode">字符串报文</param>
        /// <returns></returns>
        public byte[] WeaveDataFrame(string CmdCode, string BsCode, string DataDomain, bool HaveUserCode, bool HavePwd, bool BlnBlock, out string BwCode)
        {

            BwCode = "";

            string sTrAdr = FormatData(meterAddr, 12);

            string sTrBs = "";       //标示符
            string sTrData = "";     //数据


            string HexString = "";

            HexString = string.Format("68{0}68{1}"
                                    , DLT6452007.DataTurn(sTrAdr)         //表地址12位并且翻转
                                    , FormatData(CmdCode, 2));     //命令码2位（1个字节）

            if (BsCode != "")
            {
                sTrBs = DLT6452007.Add33HAndDataTurn(BsCode);
            }

            if (DataDomain != "")             //数据域具体内容组合
            {
                if (BlnBlock)
                {
                    string[] TmpData = DataDomain.Split('|');     //如果是块的话就需要分割因为传进来的块要求用管道符号分割

                    for (int i = 0; i < TmpData.Length; i++)
                    {
                        sTrData += DLT6452007.Add33HAndDataTurn(TmpData[i]);
                    }
                }
                else
                {
                    sTrData = DLT6452007.Add33HAndDataTurn(DataDomain);        //不是块的话数据域就直接+33翻转
                }
            }
            string TmpHex = "";

            if (HaveUserCode)       //如果数据域中包括操作者代码
            {
                TmpHex = DLT6452007.Add33HAndDataTurn(userCode);

                sTrData = TmpHex + sTrData;
            }


            if (HavePwd)        //如果数据域中还包括密码
            {
                TmpHex = DLT6452007.Add33HAndDataTurn(writeLevel) + DLT6452007.Add33HAndDataTurn(writePwd);

                sTrData = TmpHex + sTrData;
            }

            DataDomain = sTrBs + sTrData;           //组合数据域

            HexString += string.Format("{0:X2}{1}"
                                    , (int)DataDomain.Length / 2        //数据域长度
                                    , DataDomain);          //数据域

            HexString += string.Format("{0}16", DLT6452007.SumCS(HexString));    //加入效验码


            BwCode = HexString;         //报文数据

            return DLT6452007.SendDataFrame(ref HexString, FECount);        //返回数据帧
        }

        /// <summary>
        /// 解析从电能表返回的帧数据
        /// </summary>
        /// <param name="BinaryData">字节组数据</param>
        /// <param name="ExplainData">解析出的数据结构体</param>
        /// <param name="IsAQRZ">是不是安全认证（智能表专用）</param>
        /// <returns></returns>
        public static void ExplainDataFrame(byte[] BinaryData, bool IsAQRZ, out Struct.StExplain ExplainData)
        {
            List<byte> TmpValues = BinaryData.ToList();

            ExplainData = new Struct.StExplain();
            try
            {
                ExplainData.BwCode = DLT6452007.ByteToString(BinaryData);            //获取报文
            }
            catch
            {
                ExplainData.BwCode = "错误的返回数据...";
                ExplainData.BlnErr = true;
                ExplainData.ErrDisString = ExplainData.BwCode;
                return;
            }

            int intStart = TmpValues.FindIndex(item => item == 0x68);//(delegate(byte Item) { return Item == 0x68; });

            if (intStart == -1)
            {
                ExplainData.ErrDisString = "数据格式错误";
                ExplainData.BlnErr = true;
                return;
            }

            if (TmpValues.Count - intStart < 12)
            {
                ExplainData.BlnErr = true;
                ExplainData.ErrDisString = "错误的数据帧， 数据帧不完整";
                return;
            }

            if (TmpValues[TmpValues.Count - 1] != 0x16)
            {
                ExplainData.BlnErr = true;
                ExplainData.ErrDisString = "错误的数据帧， 没有结束符";
                return;
            }

            if (TmpValues[intStart + 6 + 1] != 0x68)   //第一个68后6个元素为表地址，再后一个元素为68，所以是从第一个68的下标+7
            {
                ExplainData.ErrDisString = "数据格式错误";
                ExplainData.BlnErr = true;
                return;
            }

            TmpValues.RemoveRange(0, intStart);     //把FE去掉


            ExplainData.MeterAdr = DLT6452007.DataTurn(ExplainList(TmpValues, 1, 6));       //解析出表地址

            ExplainData.CmdCode = string.Format("{0:X2}", TmpValues[8]);         //控制码

            #region ------------数据部分解析----------------


            if ((TmpValues[8] & (1 << 6)) > 0)     //表示异常应答(0xC0)
            {
                ExplainData.BlnErr = true;
                ExplainData.Lenght = (int)TmpValues[9];
                if (TmpValues[9] == 0x1)        //如果数据域长度为1，该帧才是正确帧，否则都无法处理
                {
                    ErrDictrony07 Err07 = (ErrDictrony07)((int)(TmpValues[10] - 0x33));   //解释错误
                    ExplainData.ErrDisString = Err07.ToString();
                    ExplainData.ErrNum = string.Format("{0:X2}", TmpValues[10] - 0x33);          //错误号
                }
                else if (TmpValues[9] == 0x2 && IsAQRZ)        //如果是安全认证并且长度等于2
                {
                    ErrDictrony07AnQuan A07 = (ErrDictrony07AnQuan)((int)(TmpValues[10] - 0x33));    //错误解释
                    ExplainData.ErrDisString = A07.ToString();
                    ExplainData.ErrNum = string.Format("{0:X2}{1:X2}", TmpValues[11] - 0x33, TmpValues[10] - 0x33);
                }
                else
                {
                    ExplainData.ErrDisString = "无法解析错误，请查看具体报文";
                }
            }
            else    //正常应答（正常应答分为有后续帧A0，和无后续帧80）
            {
                ExplainData.BlnErr = false;
                if ((TmpValues[8] & (1 << 7)) > 0)  //0x80
                {
                    //ExplainData.BlnFollowFrame = false;
                }
                else   //(TmpValues[8] & (1<<5))>0
                {
                    //ExplainData.BlnFollowFrame = true;
                }
                ExplainData.Lenght = (int)TmpValues[9];     //数据域长度

                if (ExplainData.Lenght == 0)
                {
                    return;        //如果数据域长度为0，则直接退出了

                }

                if (ExplainData.Lenght + 12 != TmpValues.Count)
                {
                    ExplainData.BlnErr = true;
                    ExplainData.ErrDisString = "错误的帧数据，具体请查看报文";
                    return;
                }

                ExplainData.ReturnData = DLT6452007.Cut33H(ExplainList(TmpValues, 10, ExplainData.Lenght));        //数据域-33H并翻转

            }

            #endregion

        }


        /// <summary>
        /// 解析字节列表
        /// </summary>
        /// <param name="ListValues"></param>
        /// <param name="StarIndex"></param>
        /// <param name="LastIndex"></param>
        /// <returns></returns>
        private static string ExplainList(List<byte> ListValues, int StarIndex, int Length)
        {
            string TmpValue = "";
            for (int i = StarIndex, j = 0; j < Length && j < ListValues.Count && i < ListValues.Count; j++, i++)
            {
                TmpValue += string.Format("{0:X2}", ListValues[i]);
            }

            return TmpValue;
        }


        /// <summary>
        /// 格式化数据
        /// </summary>
        /// <param name="vData">被格式化的数据</param>
        /// <param name="Len">需要截取的长度</param>
        /// <returns></returns>
        public static string FormatData(string vData, int Len)
        {
            if (Len > vData.Length)
            {
                return vData.PadLeft(Len, '0');
            }
            else if (Len == vData.Length)
            {
                return vData;
            }
            else
            {
                return vData.Substring(vData.Length - Len);
            }
        }

        /// <summary>
        /// 数据翻转
        /// </summary>
        /// <param name="vData">需要翻转的数据</param>
        /// <returns></returns>
        public static string DataTurn(string vData)
        {
            if (vData.Length % 2 != 0)
            {
                vData = string.Format("0{0}", vData);
            }

            string NewData = "";
            for (int i = 0; i < (int)vData.Length / 2; i++)
            {
                NewData = vData.Substring(i * 2, 2) + NewData;
            }
            return NewData;
        }

        /// <summary>
        /// 加0x33并且进行数据翻转
        /// </summary>
        /// <param name="vData">数据加0x33的16进制数据字符串</param>
        /// <returns></returns>
        public static string Add33HAndDataTurn(string vData)
        {
            if (vData.Length % 2 != 0)
            {
                vData = string.Format("0{0}", vData);
            }

            string NewData = "";

            for (int i = 0; i < (int)vData.Length / 2; i++)
            {
                int Int_Byt = ((int.Parse(vData.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber) + (int)0x33)) % 256;      //加33操作
                NewData = string.Format("{0:X2}{1}", Int_Byt, NewData);         //翻转组包
            }
            return NewData;
        }

        /// <summary>
        /// 减0x33并且进行数据翻转
        /// </summary>
        /// <param name="vData">数据减0x33的16进制数据字符串</param>
        /// <returns></returns>
        public static string Cut33H(string vData)
        {
            if (vData.Length == 0) return "";

            if (vData.Length % 2 != 0)
            {
                vData = string.Format("0{0}", vData);
            }

            string NewData = "";

            for (int i = 0; i < (int)vData.Length / 2; i++)
            {
                int Int_Byt = ((int.Parse(vData.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber) + (int)0x100 - (int)0x33)) % 256;      //减33操作
                NewData = string.Format("{0:X2}{1}", Int_Byt, NewData);         //翻转组包
            }
            return NewData;
        }

        /// <summary>
        /// 数据帧校验
        /// </summary>
        /// <param name="vData">需要校验的16进制字符串</param>
        /// <returns></returns>
        public static string SumCS(string vData)
        {
            if (vData.Length % 2 != 0)
            {
                vData = string.Format("0{0}", vData);
            }

            int Tmp_Byt = 0;

            for (int i = 0; i < (int)vData.Length / 2; i++)
            {
                Tmp_Byt = (int.Parse(vData.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber) + Tmp_Byt) % 256;
            }
            return Tmp_Byt.ToString("X2");
        }

        /// <summary>
        /// 将16进制字符串转化为2进制数组
        /// </summary>
        /// <param name="vData">16进制表示的字符串</param>
        /// <returns></returns>
        public static byte[] SendDataFrame(ref string vData)
        {
            return SendDataFrame(ref vData, 0);
        }

        /// <summary>
        /// 将16进制字符串转化为2进制数组
        /// </summary>
        /// <param name="vData">16进制表示的字符串</param>
        /// <param name="FESum">需要加的FE唤醒符个数</param>
        /// <returns></returns>
        public static byte[] SendDataFrame(ref string vData, int FESum)
        {
            for (int i = 0; i < FESum; i++)
            {
                vData = "FE" + vData;
            }

            byte[] DataFrame = new byte[(int)vData.Length / 2];

            for (int i = 0; i <DataFrame.Length; i++)
            {
                DataFrame[i] = byte.Parse(vData.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return DataFrame;
        }

        /// <summary>
        /// 将二进制数组转化为16进制表示字符串
        /// </summary>
        /// <param name="vArrData">二进制数组</param>
        /// <returns></returns>
        public static string ByteToString(byte[] vArrData)
        {
            return BitConverter.ToString(vArrData).Replace("-", "");
        }

        private enum ErrDictrony07
        {
            未知错误 = 0,
            其他错误 = 1,
            无请求数据 = 2,
            密码错 = 4,
            通信速率不能更改 = 8,
            年时区数超 = 16,
            日时段数短 = 32,
            费率数超出 = 64,

        }

        private enum ErrDictrony07AnQuan
        {
            未知错误 = 0,
            其他错误 = 1,
            重复充值 = 2,
            ESAM验证失败 = 4,
            身份认证失败 = 8,
            客户编号不匹配 = 16,
            充值次数错误 = 32,
            够电超囤积 = 64
        }
    }
}
